/* ------------------------------------------------------------------------ */
/* BMPA2FC.C (C) Copyright Bill Buckels 2009                                */
/* All Rights Reserved.                                                     */
/*                                                                          */
/* Licence Agreement                                                        */
/* -----------------                                                        */
/*                                                                          */
/* You have a royalty-free right to use, modify, reproduce and              */
/* distribute this source code in any way you find useful,                  */
/* provided that you agree that Bill Buckels has no warranty obligations    */
/* or liability resulting from said distribution in any way whatsoever.     */
/* If you don't agree, remove this source code from your computer now.      */
/*                                                                          */
/* Written by   : Bill Buckels                                              */
/* Email:         bbuckels@escape.ca                                        */
/*                                                                          */
/* Purpose      : This utility will allow you to convert to 				*/
/*                Apple II Double Hi-Res 140 x 192 x 16 color images		*/
/*                from IBM-PC graphics files in the following formats:		*/
/*																			*/
/*                CGA 320 x 200 x 4 color BASIC BSAVED IMAGE (.BAS) Files 	*/
/*                CGA 320 x 200 x 4 color ZSOFT .PCX Files					*/
/*				  EGA 320 x 200 x 16 color Windows .BMP Files				*/
/*																			*/
/* Revision     : 1.0 First Release                                         */
/* ------------------------------------------------------------------------ */
/* Written in Large Model 16 bit Microsoft C (MSC) Version 8.00c            */
/* Note: Run in an MS-DOS emulator like DOSBox if you can't run it raw.     */
/* ------------------------------------------------------------------------ */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <dos.h>
#include <bios.h>
#include <io.h>
#include <malloc.h>
#include <conio.h>

/* ------------------------------------------------------------------------ */
/* Declarations, Vars. etc.                                                 */
/* ------------------------------------------------------------------------ */

typedef unsigned char     uchar;
typedef unsigned int      uint;
typedef unsigned long     ulong;

uchar *szTitle[]= {
"                                   ",
" BMPA2FC(C)                        ",
" Copyright Bill Buckels 2009       ",
" All Rights Reserved.              ",
" Distributed as FreeWare.          ",
" Email: bbuckels@escape.ca         ",
"                                   ",
" Press Any Key To Continue...      ",
" --------------------------------- ",
" To Position Clip Box - Arrow Keys ",
" Page Up, Page Down, Home, and End ",
"                                   ",
" 'X' to toggle Scaling             ",
" 'S' or [ENTER] to  Save           ",
" 'Q' or [ESC] to Quit              ",
" 'H' For Help                      ",
"                                   ",
" 0-9 Toggle Color (Numeric Keys)   ",
" A-F Toggle Color (Alpha Keys)     ",
"                                   ",
NULL};

uchar *szTextTitle =
    "BMPA2FC(C) Version 1.0 Copyright Bill Buckels 2009\n"
    "All Rights Reserved.";

#define TRUE     1
#define FALSE    0
#define SUCCESS  0
#define VALID    SUCCESS
#define FAILURE  -1
#define INVALID  FAILURE

#define MCGA '\x13'
#define TEXT '\x03'

#define ASCIIZ     '\x00'
#define CRETURN    '\r'
#define LFEED      '\n'

#define ENTERKEY   '\x0d' /* character generated by the Enter Key          */
#define ESCKEY     '\x1b' /* character generated by the Esc key            */
#define FUNCKEY    '\x00' /* first character generated by function keys    */
#define UPARROW    'H'    /* second character generated by up-arrow key    */
#define DOWNARROW  'P'    /* second character generated by down-arrow key  */
#define LTARROW    'K'    /* second character generated by left-arrow key  */
#define RTARROW    'M'    /* second character generated by right-arrow key */
#define PGUP       'I'    /* second character generated by page up key     */
#define PGDOWN     'Q'    /* second character generated by page down key   */
#define HOMEKEY    'G'    /* second character generated by home key        */
#define ENDKEY     'O'    /* second character generated by end key         */

/* second character generated by numerical fkeys */
/* starting at character 59                      */
/* ending at character 68                        */
#define F1         ';'
#define F2         '<'
#define F3         '='
#define F4         '>'
#define F5         '?'
#define F6         '@'
#define F7         'A'
#define F8         'B'
#define F9         'C'
#define F10        'D'

#define FRAMESIZE ((unsigned)64000)
#define FRAMEADDR ((uchar *)0xa0000000l)

/* middle of screen */
#define XMIN 0
#define YMIN 0
#define XMOS 159
#define YMOS 99
#define XMAX 319
#define YMAX 199

#define SCREENWIDTH  (XMAX + 1)
#define RASTERWIDTH  SCREENWIDTH
#define SCREENHEIGHT (YMAX + 1)
#define CELL_SIZE    8


// some "helpful" macros

#define vload() memcpy(FRAMEADDR,(uchar *)&rawbuffer[0],FRAMESIZE)
#define vsave() memcpy((uchar *)&rawbuffer[0],FRAMEADDR,FRAMESIZE)
#define zap(x) memset(FRAMEADDR,x,FRAMESIZE)
#define getpixel(x,y) rawbuffer[(y*RASTERWIDTH)+x]


// function prototypes

uchar GetMcgaPaletteIndex(uint cgacolor),
      lsb(uint),
      msb(uint),
      SetCrtMode(uchar);

uint byteword(uchar, uchar);

int BMP16_Read(uchar *),
    BSAVE_Read(uchar *),
    CheckForCGAPCX(uchar *),
    EatKeys(),
    GetVGAIndex(uchar, uchar, uchar),
    LoadPalette(void),
    MakeBMP(uchar *, uchar *),
    PCX_Read(uchar *),
    savedhrfragment(uchar *, int, int, int);

void dhrplot(int,int,uchar),
     LineBox(int, int, int, int, uint),
     PCMidFont(uchar *, int, int, int, int, int),
     PCRomFont(uchar *, int, int, int, int, int),
     PutPixel(int, int, uint, uchar *),
     SetPalette(),
     ShowTitle(void),
     TogglePalette(uchar),
     XBox(int, int, int, int),
     XPixel(int, int, uchar *);

uchar outlinecolor;
uchar drawcolor;
uchar *rawbuffer;


#define NUM_MCGA_COLORS    256
#define NUM_RGB_COLORS     3

uchar rgbinfo[NUM_MCGA_COLORS][NUM_RGB_COLORS];  /* the vga palette */

enum {  BLACK = 0,
        BLUE,
        GREEN,
        CYAN,
        RED,
        MAGENTA,
        BROWN,
        WHITE,
        GRAY,
        LBLUE,
        LGREEN,
        LCYAN,
        LRED,
        LMAGENTA,
        YELLOW,
        BWHITE,
        NUM_VGA_COLORS};


/* the following 4 palettes are used to troll
   for standard colors in the order given below */

/* 16 Color BMP style palette (probably PBRUSH from Windows 3.1) */
unsigned char rgbBmpArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
0x00, 0x00, 0x00,    // BLACK
0x00, 0x00, 0xBF,    // BLUE
0x00, 0xBF, 0x00,    // GREEN
0x00, 0xBF, 0xBF,    // CYAN
0xBF, 0x00, 0x00,    // RED
0xBF, 0x00, 0xBF,    // MAGENTA
0xBF, 0xBF, 0x00,    // BROWN
0xC0, 0xC0, 0xC0,    // WHITE
0x80, 0x80, 0x80,    // GRAY
0x00, 0x00, 0xFF,    // LBLUE
0x00, 0xFF, 0x00,    // LGREEN
0x00, 0xFF, 0xFF,    // LCYAN
0xFF, 0x00, 0x00,    // LRED
0xFF, 0x00, 0xFF,    // LMAGENTA
0xFF, 0xFF, 0x00,    // YELLOW
0xFF, 0xFF, 0xFF};   // BWHITE

/* 16 Color BMP style palette (Windows XP) */
/* notice that these johnny-come-latelies reversed GRAY and WHITE */
unsigned char rgbXmpArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
0x00, 0x00, 0x00,  	 // BLACK
0x00, 0x00, 0x80,  	 // BLUE
0x00, 0x80, 0x00,  	 // GREEN
0x00, 0x80, 0x80,  	 // CYAN
0x80, 0x00, 0x00,  	 // RED
0x80, 0x00, 0x80,  	 // MAGENTA
0x80, 0x80, 0x00,  	 // BROWN
0x80, 0x80, 0x80,  	 // GRAY
0xC0, 0xC0, 0xC0,  	 // WHITE
0x00, 0x00, 0xFF,  	 // LBLUE
0x00, 0xFF, 0x00,  	 // LGREEN
0x00, 0xFF, 0xFF,  	 // LCYAN
0xFF, 0x00, 0x00,  	 // LRED
0xFF, 0x00, 0xFF,  	 // LMAGENTA
0xFF, 0xFF, 0x00,  	 // YELLOW
0xFF, 0xFF, 0xFF};	 // BWHITE

// VGA Palette
unsigned char rgbVgaArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
0x00, 0x00, 0x00,            /* BLACK    */
0x00, 0x00, 0xFF,            /* BLUE     */
0x00, 0xFF, 0x00,            /* GREEN    */
0x00, 0xFF, 0xFF,            /* CYAN     */
0xFF, 0x00, 0x00,            /* RED      */
0xFF, 0x00, 0xFF,            /* MAGENTA  */
0xFF, 0xFF, 0x00,            /* BROWN    */
0xC0, 0xC0, 0xC0,            /* WHITE    */
0x55, 0x55, 0x55,            /* GRAY     */
0x55, 0x55, 0xFF,            /* LBLUE    */
0x55, 0xFF, 0x55,            /* LGREEN   */
0x55, 0xFF, 0xFF,            /* LCYAN    */
0xFF, 0x55, 0x55,            /* LRED     */
0xFF, 0x55, 0xFF,            /* LMAGENTA */
0xFF, 0xFF, 0x55,            /* YELLOW   */
0xFF, 0xFF, 0xFF};           /* BWHITE   */

// 16 color ZSOFT PCPAINT PCX style palette
unsigned char rgbPcxArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
0x00, 0x00, 0x00,            /* BLACK    */
0x00, 0x00, 0xAA,            /* BLUE     */
0x00, 0xAA, 0x00,            /* GREEN    */
0x00, 0xAA, 0xAA,            /* CYAN     */
0xAA, 0x00, 0x00,            /* RED      */
0xAA, 0x00, 0xAA,            /* MAGENTA  */
0xAA, 0xAA, 0x00,            /* BROWN    */
0xAA, 0xAA, 0xAA,            /* WHITE    */
0x55, 0x55, 0x55,            /* GRAY     */
0x55, 0x55, 0xFF,            /* LBLUE    */
0x55, 0xFF, 0x55,            /* LGREEN   */
0x55, 0xFF, 0xFF,            /* LCYAN    */
0xFF, 0x55, 0x55,            /* LRED     */
0xFF, 0x55, 0xFF,            /* LMAGENTA */
0xFF, 0xFF, 0x55,            /* YELLOW   */
0xFF, 0xFF, 0xFF};           /* BWHITE   */


/* this palette is in the actual apple II lores color order */
/* this shows us approximately what we will end-up with */
uchar rgbDoubleHiresArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
  0,   0,   0,	/* black    */
208,   0,  48,	/* red      */
  0,   0, 128,	/* dk blue  */
255,   0, 255,	/* purple   */
  0, 128,   0,	/* dk green */
128, 128, 128,	/* gray     */
  0,   0, 255,	/* med blue */
 96, 160, 255,	/* lt blue  */
128,  80,   0,	/* brown    */
255, 128,   0,	/* orange   */
192, 192, 192,	/* grey     */
255, 144, 128,	/* pink     */
  0, 255,   0,	/* lt green */
255, 255,   0,	/* yellow   */
 64, 255, 144,	/* aqua     */
255, 255, 255};	/* white    */

#define LOBLACK   	0
#define LORED     	1
#define LODKBLUE 	2
#define LOPURPLE  	3
#define LODKGREEN	4
#define LOGRAY    	5
#define LOMEDBLUE	6
#define LOLTBLUE 	7
#define LOBROWN   	8
#define LOORANGE  	9
#define LOGREY    	10
#define LOPINK    	11
#define LOLTGREEN	12
#define LOYELLOW  	13
#define LOAQUA    	14
#define LOWHITE   	15

unsigned char VGAtoApple[NUM_VGA_COLORS]={
	LOBLACK,
	LODKBLUE,
	LODKGREEN,
	LOMEDBLUE,
	LORED,
	LOPURPLE,
	LOBROWN,
	LOGREY,
	LOGRAY,
	LOLTBLUE,
	LOLTGREEN,
	LOAQUA,
	LOORANGE,
	LOPINK,
	LOYELLOW,
	LOWHITE};

// to rollback the colors in case we can't automatically assign
unsigned char VGAtoAppleSaved[NUM_VGA_COLORS]={
	LOBLACK,
	LODKBLUE,
	LODKGREEN,
	LOMEDBLUE,
	LORED,
	LOPURPLE,
	LOBROWN,
	LOGREY,
	LOGRAY,
	LOLTBLUE,
	LOLTGREEN,
	LOAQUA,
	LOORANGE,
	LOPINK,
	LOYELLOW,
	LOWHITE};


/* our working copy of the apple II double hires colors */
/* this is in Apple II order */
uchar rgbArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
  0,   0,   0,	/* black    */
208,   0,  48,	/* red      */
  0,   0, 128,	/* dk blue  */
255,   0, 255,	/* purple   */
  0, 128,   0,	/* dk green */
128, 128, 128,	/* gray     */
  0,   0, 255,	/* med blue */
 96, 160, 255,	/* lt blue  */
128,  80,   0,	/* brown    */
255, 128,   0,	/* orange   */
192, 192, 192,	/* grey     */
255, 144, 128,	/* pink     */
  0, 255,   0,	/* lt green */
255, 255,   0,	/* yellow   */
 64, 255, 144,	/* aqua     */
255, 255, 255};	/* white    */

/* 16 Color VGA style palettes with RGB values
   from corresponding Apple II double hires colors */
unsigned char rgbOriginalArray[NUM_VGA_COLORS][NUM_RGB_COLORS]={
  0,   0,   0,    // BLACK    - black
  0,   0, 128,    // BLUE     - dk blue
  0, 128,   0,    // GREEN    - dk green
  0,   0, 255,    // CYAN     - med blue
208,   0,  48,    // RED      - red
255,   0, 255,    // MAGENTA  - purple
128,  80,   0,    // BROWN    - brown
192, 192, 192,    // WHITE    - grey
128, 128, 128,    // GRAY     - gray
 96, 160, 255,    // LBLUE    - lt blue
  0, 255,   0,    // LGREEN   - lt green
 64, 255, 144,    // LCYAN    - aqua
255, 128,   0,    // LRED     - orange
255, 144, 128,    // LMAGENTA - pink
255, 255,   0,    // YELLOW   - yellow
255, 255, 255};   // BWHITE   - white

enum {  CGA_BLACK = 0,
        CGA_CYAN,
        CGA_MAGENTA,
        CGA_WHITE,
        NUM_CGA_COLORS};

/* a microsoft compatible bsaved image format descriptor */
uchar BSAVED_header[7]={
    '\xfd','\x00','\xb8','\x00','\x00','\x00','\x40'};


/* bmiHeader.biClrUsed and bmiHeader.biClrImportant fields
   will vary depending on the process that created the bmp
   but the other fields (below) should be invariant. */
uchar BMP_checkheader[] ={
0x42, 0x4D, 0x76, 0x7D, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xC8, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


/* ------------------------------------------------------------------------ */
/* Low Level Video Routines, drawing routines, etc.                         */
/* ------------------------------------------------------------------------ */

uchar SetCrtMode(uchar vidmode)
{
  union REGS inregs, outregs;

  /* set mode */
  inregs.h.ah = 0;
  inregs.h.al = vidmode;
  int86(0x10, &inregs, &outregs);

  /* get mode */
  inregs.h.ah = 0xf;
  int86(0x10, &inregs, &outregs);

  /* return mode */
  return outregs.h.al;

}

int LoadPalette()
{
  union REGS regs;
  struct SREGS segregs;

  regs.h.ah = 0x10;                    /* fuction 10h */
  regs.h.al = 0x12;
  /* subfunction 12h - set block of color registers */
  regs.x.bx = 0;                       /* start with this reg */
  regs.x.cx = NUM_MCGA_COLORS;         /* do this many */
  regs.x.dx = (uint)rgbinfo;   /* offset to array */
  segregs.es = (uint)((long)rgbinfo >> 16);
  /* segment of array */
  int86x(0x10, &regs, &regs, &segregs);/* dump data to color registers */

  return SUCCESS;

}



/* ---------------------------------------------------------------------- */
/* Write a pixel at x,y using color                                       */
/* ---------------------------------------------------------------------- */
void PutPixel(int x, int y,uint pixelvalue, uchar *framebuffer)
{
    if (x<XMIN || x>XMAX || y<YMIN || y>YMAX)
      return;

    framebuffer[(y*RASTERWIDTH)+x]=(uchar)pixelvalue;
}


void XPixel(int x, int y, uchar *framebuffer)
{

    if (x<XMIN || x>XMAX || y<YMIN || y>YMAX)
      return;

    framebuffer[(y*RASTERWIDTH)+x] = framebuffer[(y*RASTERWIDTH)+x]^0xff;
}


void XBox(int x1, int y1, int x2, int y2)
{
   int x, y;

   for (x = x1; x <= x2; x++)XPixel(x, y1, FRAMEADDR);
   for (y = (y1+1); y < y2; y++) {
     XPixel(x1, y, FRAMEADDR);
     XPixel(x2, y, FRAMEADDR);
   }
   for (x = x1; x <= x2; x++)XPixel(x, y2, FRAMEADDR);
   return;
}


void LineBox(int x1, int y1, int x2, int y2, uint pixelvalue)
{
   int x, y;

   for (x = x1; x <= x2; x++)PutPixel(x, y1, pixelvalue, FRAMEADDR);
   for (y = (y1+1); y < y2; y++) {
     PutPixel(x1, y, pixelvalue, FRAMEADDR);
     PutPixel(x2, y, pixelvalue, FRAMEADDR);
   }
   for (x = x1; x <= x2; x++)PutPixel(x, y2, pixelvalue, FRAMEADDR);
   return;
}

/* ---------------------------------------------------------------------- */
/* PCRomFont                                                              */
/* Uses the rom font as a template for a bitmap font.                     */
/* This allows us to map a font using scaling techniques on any PC        */
/* without the need for an external font file or a bios supported font.   */
/* This was more prevalent in days gone by than it is today, of course.   */
/* ---------------------------------------------------------------------- */
void PCRomFont(uchar *str,
               int xorigin, int yorigin, int scale,
               int fontcolor, int outlinecolor)
{
    int scanline,
        yreg=yorigin,
        xreg=xorigin,
        byt,
        character,
        nibble,
        color;
    int x,y;

    /* flags etcetera */

    uchar *romfont=(uchar *) 0xffa6000el;
    /* a pointer to the 8 x 8 BITMAPPED font in the PC ROM */

    int target = strlen(str);  /* string length */

    for (scanline=0;scanline<CELL_SIZE;scanline++)/* finish the current scanline*/
    {                                     /*  before advancing to the next*/
      for (byt=0;byt<target;byt++)     /* run the scanline*/
      {
        /* get the bitmap  */
        character = romfont[(str[byt]&0x7f)*CELL_SIZE+scanline];
        for (nibble=0;nibble<CELL_SIZE;nibble++)
        {
          xreg+=scale;
          /* chew the byte to bits and lite the pixel if it's a swallow  */
          if (str[byt]!=CRETURN && str[byt]!=LFEED) {
            if (character & 0x80>>nibble)
              color = fontcolor;
            else
              color = outlinecolor;
            if (color > -1 ) {
              for (x=0; x!=scale; x++)
                for (y=0;y!=scale;y++)
                  PutPixel(xreg+x,yreg+y,color, FRAMEADDR);
            }
          }
        }
      }
      yreg+=scale;
      xreg=xorigin;
    }

}

/* ---------------------------------------------------------------------- */
/* PCMidFont                                                              */
/* Maps to PCRomfont, uses a centre-justified x-coordinate.               */
/* ---------------------------------------------------------------------- */
void PCMidFont(uchar *str, int xmiddle, int yorigin,
               int scale, int fontcolor, int outlinecolor)
{   /* centre justified string */
    PCRomFont(str,(xmiddle-(4*(strlen(str))*scale)),yorigin,scale,
              fontcolor, outlinecolor);
}

/* ---------------------------------------------------------------------- */
/* GetMcgaPalette is called during the loading of the input file.         */
/* ---------------------------------------------------------------------- */
uchar GetMcgaPaletteIndex(uint cgacolor)
{

  // I just hardcoded these in here for CGA images.
  // I couldn't see any point in getting too gancy with 4 colors
  // There isn't much hardship in toggling 4 colors anyway
  // My main target for automation was when a 16 color BMP is colored
  // in Windows Paint... then exported in this thing.
  uint uiIndex;

  switch(cgacolor) {
    case  CGA_WHITE:
          uiIndex = LOWHITE; break;
    case  CGA_MAGENTA:
          uiIndex = LORED; break;
    case  CGA_CYAN:
          uiIndex = LODKBLUE; break;
    case  CGA_BLACK:
    default:
          uiIndex = LOBLACK; break;
  }
  return (uchar)uiIndex;
}


void SetPalette()
{

  uint idx, x, temp;

	memset((uchar *)&rgbinfo[0][0], 0, (NUM_MCGA_COLORS*NUM_RGB_COLORS));

	/* Set the lightest and darkest color in the current palette.  */
	/* use the darkest color for the drawcolor                     */
	/* use the lightest color for the outline color                */

	drawcolor    = 254;
	outlinecolor = 255;

	// using 6 bit color model (VGA standard video uses 6 bits)
	// for the internal program. values are in the range 0-63
	rgbinfo[255][0] = rgbinfo[255][1] =  rgbinfo[255][2] = 63;

	// leave the drawcolor and outline colors alone
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
		for (x = 0; x < NUM_RGB_COLORS; x++) {
		   temp = rgbArray[idx][x];
		   rgbinfo[idx][x] = temp >> 2; // downshift to 6 bits of color
		}
	}

}


/* the following attempts to match BMP colors with a common palette */
/* that is to say... a basic default EGA style 16 color palette like */
/* the kind that Windows Paint provides as a lowest common denominator */
/* my rationale here is that if we can't automate the entire remapping */
/* we fail the whole sh*terree because otherwise we could start mashing */
/* two colors or more together and I for one would not care for that... */
int GetVGAIndex(uchar red, uchar green, uchar blue)
{

    int idx;

    // try exact match
    for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
		if (red == rgbBmpArray[idx][0] && green == rgbBmpArray[idx][1] && blue == rgbBmpArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
		if (red == rgbXmpArray[idx][0] && green == rgbXmpArray[idx][1] && blue == rgbXmpArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
        if (red == rgbVgaArray[idx][0] && green == rgbVgaArray[idx][1] && blue == rgbVgaArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
        if (red == rgbPcxArray[idx][0] && green == rgbPcxArray[idx][1] && blue == rgbPcxArray[idx][2])return idx;
	}

	// adjust gun values to match using EGA-like thresholds
	// this corresponds to the old PCX style palette
	if (red < 43) red = 0;         // 0x00
	else if (red < 128) red = 85;  // 0x55
	else if (red < 224) red = 170; // 0xaa
	else red = 255;                // 0xff

	if (green < 43) green = 0;
	else if (green < 128) green = 85;
	else if (green < 213) green = 170;
	else green = 255;

	if (blue < 43) blue = 0;
	else if (blue < 128) blue = 85;
	else if (blue < 213) blue = 170;
	else blue = 255;

    // try again
    for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
		if (red == rgbBmpArray[idx][0] && green == rgbBmpArray[idx][1] && blue == rgbBmpArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
		if (red == rgbXmpArray[idx][0] && green == rgbXmpArray[idx][1] && blue == rgbXmpArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
        if (red == rgbVgaArray[idx][0] && green == rgbVgaArray[idx][1] && blue == rgbVgaArray[idx][2])return idx;
	}
	for (idx = 0; idx < NUM_VGA_COLORS; idx++) {
        if (red == rgbPcxArray[idx][0] && green == rgbPcxArray[idx][1] && blue == rgbPcxArray[idx][2])return idx;
	}
	// if no match yet let them pick double hires color manually
    return INVALID;
}


int PaletteIndex[NUM_VGA_COLORS] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

void TogglePalette(uchar ch)
{
	/* after the initial remapping of colors by color order */
	/* it is very likely that not all entries will remap correctly */
	/* so the palette can be toggled to adjust this */
	int idx, jdx;

	ch = toupper(ch);

	switch(ch)
	{
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		  idx = ch - 55;
		  break;

		default:

		    if (ch < '0' || ch > '9')return;
		    idx = ch - 48;
	}

	/* update remapping index */
    jdx = PaletteIndex[idx] + 1;
    if (jdx > 15)jdx = 0;
    PaletteIndex[idx] = jdx;
    /* update the working array */
    rgbArray[idx][0] = rgbDoubleHiresArray[jdx][0];
    rgbArray[idx][1] = rgbDoubleHiresArray[jdx][1];
    rgbArray[idx][2] = rgbDoubleHiresArray[jdx][2];
    /* update the visual */
    SetPalette();
    LoadPalette();

}


/* ---------------------------------------------------------------------- */
/* File Related Functions                                                 */
/* Image Loaders, Image Savers, etc.                                      */
/* ---------------------------------------------------------------------- */

/* type conversion functions */
uint byteword(uchar a, uchar b){
  return b << 8 | a;
}
uchar lsb(uint word){
  return word &0xff;
}
uchar msb(uint word){
  return word >> 8;
}

int CheckForCGAPCX(uchar *name)
{
  FILE *fp;
  /* reads a ZSOFT .PCX header but ignores the color map */
  int i;
  /* we only want CGA COLOR compatible full screens. */

  uchar pcxheader[128];
  uint zsoft,version,codetype,pixbits;
  uint xmin, ymin, xmax, ymax;
  uint x, y;
  uint no_planes, bytesperline;
  int status = VALID;

  /* read the file header */
  if((fp = fopen(name, "rb")) == NULL)return INVALID;
  for(i = 0;i < 128;i++)pcxheader[i] = fgetc(fp);
  fclose(fp);

  zsoft = pcxheader[0];
  version = pcxheader[1];
  codetype = pcxheader[2];
  pixbits = pcxheader[3];

  if(zsoft != 10)
    status = INVALID;
  if(codetype != 1)
    status = INVALID;
  if(pixbits != 2)        /* accept only CGA color images */
    status = INVALID;     /* monochrome images can't be mapped properly */

  xmin = byteword(pcxheader[4], pcxheader[5]);
  ymin = byteword(pcxheader[6], pcxheader[7]);
  xmax = byteword(pcxheader[8], pcxheader[9]);
  ymax = byteword(pcxheader[10], pcxheader[11]);

  no_planes = pcxheader[65];
  bytesperline = byteword(pcxheader[66], pcxheader[67]);

  x =  xmax - xmin;
  y =  ymax - ymin;

  if(x != XMAX)status = INVALID;
  if(y != YMAX)status = INVALID;
  if(no_planes != 1)status = INVALID;
  if(bytesperline != 80)status = INVALID;    /* full screens only */

  /* we can ignore the color map since we        */
  /* are limiting ourselves to CGA modes         */
  /* so we will not handle over 2-bits per pixel */

  return status;

}

int BSAVE_Read(uchar *name)
{
  int fh,fh2,y,i;
  uchar byte;
  uchar *crt;
  uchar namebuf[128];
  uchar headbuf[7];
  uchar linebuffer[80];
  long target = 16384l,target2,header = 7;

  /* open the file twice,eliminate the interleaf,read contiguous */
  sprintf(namebuf, "%s.BAS", name);

  if((fh = open(namebuf, O_RDONLY | O_BINARY)) == - 1)return INVALID;

  if((fh2 = open(namebuf, O_RDONLY | O_BINARY)) == - 1) {
    close(fh);
    return INVALID;
  }

  read(fh, headbuf, sizeof(BSAVED_header));

  /* only read the first 3 bytes, some of these are a little */
  /* different size, depending on how they were saved.       */

  for(i = 0;i < 3;i++)
    if(headbuf[i] != BSAVED_header[i]) {
      close(fh);
      close(fh2);
      return INVALID;
    }

  target2 = (target / 2) + header;
  lseek(fh, (long)(header), SEEK_SET)  ;
  lseek(fh2, (long)(target2), SEEK_SET);

  crt = (uchar *)&rawbuffer[0];

  /* translate from a 2 bit color pixel to an 8 bit color pixel */

  for(y = 0;y < SCREENHEIGHT;y += 2) {
    read(fh, linebuffer, 80);
    for(i = 0;i < 80;i++) {
      byte = linebuffer[i];
      *crt++ = GetMcgaPaletteIndex((byte >> 6));
      *crt++ = GetMcgaPaletteIndex((byte >> 4)&3);
      *crt++ = GetMcgaPaletteIndex((byte >> 2)&3);
      *crt++ = GetMcgaPaletteIndex((byte)&3);
    }
    read(fh2, linebuffer, 80);
    for(i = 0;i < 80;i++) {
      byte = linebuffer[i];
      *crt++ = GetMcgaPaletteIndex((byte >> 6));
      *crt++ = GetMcgaPaletteIndex((byte >> 4)&3);
      *crt++ = GetMcgaPaletteIndex((byte >> 2)&3);
      *crt++ = GetMcgaPaletteIndex((byte)&3);
    }
  }
  close(fh);
  close(fh2);
  return SUCCESS;

}

/* Translation for VGA to display CGA */
int PCX_Read(uchar *pcxfilename)
{
  uchar pcxheader[128];
  uchar *crt;
  uint packet;
  uchar bit[4];
  FILE *fp;
  uchar byte,bytecount;
  long wordcount,target;
  uchar name1[128], name2[128];

  sprintf(name1, "%s.PCX", pcxfilename);

  if(CheckForCGAPCX(name1) == - 1)return INVALID;

  if (NULL == (fp = fopen(name1, "rb")))return INVALID;

  target = filelength(fileno(fp));
  for(wordcount = 0;wordcount != 128;wordcount++)byte = fgetc(fp);

  crt = (uchar *)&rawbuffer[0];

  do {
    bytecount = 1;                     /* start with a seed count */
    byte = fgetc(fp);
    wordcount++;
    /* check to see if its raw */
    if(0xC0 == (0xC0 &byte)) {
      /* if its not, run encoded */
      bytecount = 0x3f &byte;
      byte = fgetc(fp);
      wordcount++;
    }
    /* translate from 2 bit pixel to 8 bit pixel */

    bit[0] = GetMcgaPaletteIndex((byte >> 6));
    bit[1] = GetMcgaPaletteIndex((byte >> 4)&3);
    bit[2] = GetMcgaPaletteIndex((byte >> 2)&3);
    bit[3] = GetMcgaPaletteIndex((byte)&3);
    packet = 0;
    while(packet++ < bytecount) {
      *crt++ = bit[0];
      *crt++ = bit[1];
      *crt++ = bit[2];
      *crt++ = bit[3];
    }
  }while(wordcount < target);
  fclose(fp);
  return(0);
}



int BMP16_Read(uchar *basename)
{

	uint temp, temp2;
	uchar *screenbuffer, bmpfile[128];
	int x, y;
	FILE *fp;

    sprintf(bmpfile,"%s.BMP",basename);
	fp=fopen(bmpfile,"rb");

	if (NULL == fp) return INVALID;

    /* check the invariant fields in the bmp header */
	for (x=0; x < sizeof(BMP_checkheader); x++) {
	  temp2 = fgetc(fp);
	  temp=BMP_checkheader[x];
	  if (temp != temp2) {
		fclose(fp);
		return INVALID;

	  }
	}

	/* ignore variant fields in header - 8 bytes */
	/* bmiHeader.biClrUsed and bmiHeader.biClrImportant */
	for (x=0; x < 8; x++)fgetc(fp);

   	for(y=0;y<NUM_VGA_COLORS;y++)
	{
	  /* RGB Quad Structure b,g,r,0 */
	  for(x=3;x>0;x--)
	  {
		temp = fgetc(fp);
		rgbOriginalArray[y][x-1] = temp;

	  }
	  fgetc(fp);
	}

    for(y=0;y<NUM_VGA_COLORS;y++) {
		// attempt to reorganize remapping of the BMP
		// to the apple double hires palette based on rgb values
		x = GetVgaIndex(rgbOriginalArray[y][0],
						rgbOriginalArray[y][1],
						rgbOriginalArray[y][2]);
		// however, if all colors do not match then
		// we simply use the default color order
		// and let them manually select the apple double hires color
		if (x == INVALID) {
		    for (x = 0; x < NUM_VGA_COLORS; x++)
		       VGAtoApple[x] = VGAtoAppleSaved[x]; 	// reset
		    break;

		}
		/* move x to y for the remap of the image data */
		/* if they don't like it they can toggle the colors */
	    VGAtoApple[y] = VGAtoAppleSaved[x];

	}

    /* remap the image to the equivalent apple2 double hires colors */
    /* to the best known equivalents */
	for (y = SCREENHEIGHT; y > 0; y--) {
	  screenbuffer=(uchar *)&rawbuffer[((y-1)*SCREENWIDTH)];
	  for (x = 0; x < SCREENWIDTH; x++) {
		if (x % 2 == 0) {
		  temp  = fgetc(fp);
		  temp2 = (temp >> 4);
		}
		else {
		  temp2 = (temp & 15);
		}

		screenbuffer[x] = VGAtoApple[temp2];

	  }
	}

	fclose(fp);


  	return SUCCESS;

}

/* routines to save to Apple 2 Double Hires Format */

/* provides base address for page1 hires scanlines  */
unsigned HB[]={
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0};


unsigned char dhrbuf[16384];

/*

The following is logically reordered to match the lores
color order...

                                                Repeated
                                                Binary
          Color         aux1  main1 aux2  main2 Pattern
          Black          00    00    00    00    0000
          Magenta        08    11    22    44    0001
		  Dark Blue      11    22    44    08    1000
          Violet         19    33    66    4C    1001
          Dark Green     22    44    08    11    0100
          Grey1          2A    55    2A    55    0101
          Medium Blue    33    66    4C    19    1100
          Light Blue     3B    77    6E    5D    1101
          Brown          44    08    11    22    0010
          Orange         4C    19    33    66    0011
          Grey2          55    2A    55    2A    1010
          Pink           5D    3B    77    6E    1011
          Green          66    4C    19    33    0110
          Yellow         6E    5D    3B    77    0111
          Aqua           77    6E    5D    3B    1110
          White          7F    7F    7F    7F    1111





*/

/*

#define LOBLACK   	0
#define LORED     	1
#define LODKBLUE 	2
#define LOPURPLE  	3
#define LODKGREEN	4
#define LOGRAY    	5
#define LOMEDBLUE	6
#define LOLTBLUE 	7
#define LOBROWN   	8
#define LOORANGE  	9
#define LOGREY    	10
#define LOPINK    	11
#define LOLTGREEN	12
#define LOYELLOW  	13
#define LOAQUA    	14
#define LOWHITE   	15

*/

/* the following array is based on the above */
unsigned char dhrbytes[16][4] = {
	0x00,0x00,0x00,0x00,
	0x08,0x11,0x22,0x44,
	0x11,0x22,0x44,0x08,
	0x19,0x33,0x66,0x4C,
	0x22,0x44,0x08,0x11,
	0x2A,0x55,0x2A,0x55,
	0x33,0x66,0x4C,0x19,
	0x3B,0x77,0x6E,0x5D,
	0x44,0x08,0x11,0x22,
	0x4C,0x19,0x33,0x66,
	0x55,0x2A,0x55,0x2A,
	0x5D,0x3B,0x77,0x6E,
	0x66,0x4C,0x19,0x33,
	0x6E,0x5D,0x3B,0x77,
	0x77,0x6E,0x5D,0x3B,
	0x7F,0x7F,0x7F,0x7F};


/* a double hi-res pixel can occur at any one of 7 positions */
/* in a 4 byte block which spans aux and main screen memory */
/* the horizontal resolution is 140 pixels */
void dhrplot(int x,int y,uchar drawcolor)
{
    int xoff, pattern;
    unsigned char *ptraux, *ptrmain;

    pattern = (x%7);
	xoff = HB[y] + ((x/7) * 2);
    ptraux  = (unsigned char *) &dhrbuf[xoff-0x2000];
    ptrmain = (unsigned char *) &dhrbuf[xoff];


	switch(pattern)
	{
		/* left this here for reference

		unsigned char dhrpattern[7][4] = {
		0,0,0,0,
		0,0,0,1,
		1,1,1,1,
		1,1,2,2,
		2,2,2,2,
		2,3,3,3,
        3,3,3,3};
        */

		case 0: ptraux[0] |= dhrbytes[drawcolor][0] &0x0f;
		        break;
		case 1: ptraux[0] |= dhrbytes[drawcolor][0] & 0x70;
		        ptrmain[0] |= dhrbytes[drawcolor][1] & 0x01;
		        break;
		case 2: ptrmain[0] |= dhrbytes[drawcolor][1] & 0x1e;
		        break;
		case 3: ptrmain[0] |= dhrbytes[drawcolor][1] & 0x60;
		        ptraux[1] |= dhrbytes[drawcolor][2] & 0x03;
                break;
		case 4: ptraux[1] |= dhrbytes[drawcolor][2] & 0x3c;
		        break;
		case 5: ptraux[1] |= dhrbytes[drawcolor][2] & 0x40;
 		        ptrmain[1] |= dhrbytes[drawcolor][3] & 0x07;
 		        break;
		case 6: ptrmain[1] |= dhrbytes[drawcolor][3] & 0x78;
 		        break;
	}

}

/* save 2 output files */
int savedhrfragment(uchar *basename, int x1, int y1, int dhrscale)
{

	FILE *fp;
	uchar outfile[128], temp, remap;
	int x,y,x2,y2;

    sprintf(outfile,"%s.2FC",basename);
	fp = fopen(outfile,"wb");
	if (NULL == fp)return INVALID;

   	memset(dhrbuf,0,16384);
	for (y = 0; y< 192; y++) {

	   /* double scanlines to preserve aspect ratio
	      when not scaling */
	   if (dhrscale == 1) y2 = ((y/2) + y1);
	   else y2 = y + y1;

	   /* skip every second pixel when scaling */
	   for (x = 0; x < 140; x++) {
		  if (dhrscale == 1) x2 = x + x1;
		  else x2 = (x * 2) + x1;
		  temp = getpixel(x2,y2);
		  remap =  PaletteIndex[temp];
		  dhrplot(x,y,remap);
	   }

    }

	fwrite(dhrbuf,1,16384,fp);
	fclose(fp);


    // the bsaved images are split into two files
    // the first file is loaded into aux mem
    sprintf(outfile,"%s.AUX",basename);
	fp = fopen(outfile,"wb");
	if (NULL == fp)return INVALID;
	fwrite(dhrbuf,1,8192,fp);
	fclose(fp);

    // the second file is loaded into main mem
    sprintf(outfile,"%s.BIN",basename);
	fp = fopen(outfile,"wb");
	if (NULL == fp)return INVALID;
	fwrite(&dhrbuf[8192],1,8192,fp);
	fclose(fp);

	return SUCCESS;
}


/* ------------------------------------------------------------------------ */
/* User Input and helper functions and Main Program                         */
/* ------------------------------------------------------------------------ */

// eat keystrokes... avoid mindlessly cycling through the program
// if the user leans on the keyboard and doesn't budge for a moment.
// Any DOS program should do this...

int EatKeys()
{
  if (kbhit())
    while (kbhit())
      if (getch() == FUNCKEY)
         getch();

  return SUCCESS;
}

// show the title at startup and when 'H' (Help) is pressed.
void ShowTitle()
{
    int y, yorg, y1, x1, xx, c;
    uchar *ptr;

    for (y = 0; szTitle[y]!= NULL; y++);
    y+=2;


    yorg = y1 = ((SCREENHEIGHT - y*CELL_SIZE) / 2) - 1;
    for (y = 0; szTitle[y]!= NULL; y++) {
      ptr = (char *)&szTitle[y][0];
      PCMidFont(ptr, XMOS, y1, 1, drawcolor, outlinecolor);
      y1+=CELL_SIZE;
    }
    PCMidFont(ptr, XMOS, y1, 1, drawcolor, outlinecolor);
    PCMidFont(ptr, XMOS, y1+CELL_SIZE, 1, drawcolor, outlinecolor);

    // a little finishing touch for the help screen
    // to show the currently selected colors.
    xx = XMOS-136;
    c = 1;
	PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("u", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("e", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("n", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("t", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont(" ", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("o", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("l", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("o", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("r", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont("s", (xx+=8), y1, 1, (c++), drawcolor);
	PCMidFont(":", (xx+=8), y1, 1,  c,    drawcolor);
	PCMidFont(" ", (xx+=8), y1, 1,  c,    drawcolor);
    c = 0;
    PCMidFont("0", (xx+=8), y1, 1, (c++), outlinecolor);
    PCMidFont("1", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("2", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("3", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("4", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("5", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("6", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("7", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("8", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("9", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("A", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("B", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("C", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("D", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("E", (xx+=8), y1, 1, (c++), drawcolor);
    PCMidFont("F", (xx+=8), y1, 1,  c,    drawcolor);
    y1+=CELL_SIZE;
    y1+=CELL_SIZE;

    x1 = strlen(ptr) * 4;
    LineBox(XMOS - x1 + 2, yorg+1, XMOS + x1 - 1, y1-2, drawcolor);

    EatKeys();
    while (!kbhit());
    EatKeys();

}

void main(int argc, char **argv)
{
  int status = 0, idx, iMax, dhrscale = 2;

  /* bounds of hires image */
  int oldx1=20, oldy1=4, oldx2=299, oldy2=195;
  int x1=20, y1=4, x2=299, y2=195;

  uchar c, kdx;
  uchar fname[128],sname[128],outfile[128];
  uchar *wordptr;
  uchar scratchbuf[128];
  FILE *fp;

  if(argc == 1) {
    puts(szTextTitle);
    puts("Command line Usage is \"BMPA2FC MyCGA.PCX\"");
    puts("                      \"BMPA2FC MyBSAVE.BAS\"");
    puts("                      \"BMPA2FC My16Color.BMP\"");
    puts("                      \"BMPA2FC MyCGA.PCX OutfileBaseName\"");
    puts("                      \"BMPA2FC MyBSAVE.BAS OutfileBaseName\"");
    puts("                      \"BMPA2FC My16Color.BMP OutfileBaseName\"");

    printf("Enter Input FileName (Blank to Exit): ");
    gets(fname);
    if (fname[0] == ASCIIZ)
      exit(1);
    printf("Enter Output FileBaseName (Blank for None) : ");
    gets(outfile);
  }
  else {
    strcpy(fname, argv[1]);
    if (argc > 2)
      strcpy(outfile, argv[2]);
    else
      outfile[0] = ASCIIZ;
  }

  if((rawbuffer = malloc((unsigned)65000)) == NULL) {
    puts(szTextTitle);
    puts("Out of Memory...");
    exit(1);
  }


  strcpy(sname, fname);
  wordptr = strtok(sname, ".");
  if (outfile[0] == ASCIIZ)strcpy(outfile,sname);

  status = PCX_Read(sname);
  if(status)status = BSAVE_Read(sname);
  if(status)status = BMP16_Read(sname);
  if (status) {
    puts(szTextTitle);
    printf("%s is an Unsupported Format or cannot be opened.\n", fname);
    free(rawbuffer);
    exit(1);
  }

  status = ESCKEY;

  if((SetCrtMode(MCGA)) == MCGA) {
    SetPalette();
    LoadPalette();
    vload();
    ShowTitle();
    vload();
    xbox(oldx1, oldy1, oldx2, oldy2);
    do {
      c=toupper(getch());

      if (FUNCKEY == c) {
        kdx=getch();
        switch(kdx) {
		  case HOMEKEY:
		       x1 = 0;
		  case LTARROW:
		       x1-=1;
		       x2-=1;
		       if (x1 < 0) {
				 x1 = 0;
				 if (dhrscale == 1) x2 = 139;
				 else x2 = 279;
		       }
		       break;
          case ENDKEY:
               x2=319;
          case RTARROW:
		       x1+=4;
		       x2+=4;
		       if (x2 > 319) {
				 x2 = 319;
				 if (dhrscale == 1)x1 = 319 - 139;
				 else x1 = 319 - 279;
		       }
		       break;
          case PGUP:
               y1 = 0;
          case UPARROW:
		       y1-=1;
		       y2-=1;
		       if (y1 < 0) {
				 y1 = 0;
				 if (dhrscale == 1)y2 = 95;
				 else y2 = 191;
		       }
		       break;
          case PGDOWN:
               y2 = 199;
          case DOWNARROW:
		       y1+=4;
		       y2+=4;
               if (y2 > 199) {
			      y2 = 199;
			      if (dhrscale == 1)y1 = 199 - 95;
			      else y1 = 199 - 191;
			   }

		       break;
          default:
            break;
        }
      }
      else {

        if (c == 'S' || c == ENTERKEY)
          break;

        switch (c) {
			// scaling feature
		  case 'X':
		    xbox(oldx1, oldy1, oldx2, oldy2);
		    if (dhrscale == 1)
				dhrscale = 2;
			else
			    dhrscale = 1;

			x2 = x1 + (140 * dhrscale)-1;
			y2 = y1 + (96 * dhrscale)-1;
		    if (x2 > 319) {
				x2 = 319;
				if (dhrscale == 1)x1 = 319 - 139;
				else x1 = 319 - 279;
			}
            if (y2 > 199) {
				y2 = 199;
			    if (dhrscale == 1)y1 = 199 - 95;
			    else y1 = 199 - 191;
			}
			oldx1 = x1; oldx2 = x2;
			oldy1 = y1; oldy2 = y2;
			xbox(oldx1, oldy1, oldx2, oldy2);
			break;

          case 'H':
            xbox(oldx1, oldy1, oldx2, oldy2);
            ShowTitle();
            vload();
            xbox(oldx1, oldy1, oldx2, oldy2);
            break;
          case 'Q':
            c = ESCKEY;
            break;
          default:
            if (c < '0' || c > 'F') break;
            if (c > '9' && c < 'A') break;
            TogglePalette(c);
        }
      }

      if (x1!=oldx1 || y1!=oldy1) {
	     /* erase old box */
	     xbox(oldx1, oldy1, oldx2, oldy2);
	     /* update old cords with new cords */
	     oldx1 = x1; oldy1 = y1; oldx2 = x2; oldy2 = y2;
	     /* plot new box */
	     xbox(oldx1, oldy1, oldx2, oldy2);
	  }


    } while (c!=ESCKEY);

    if(c!=ESCKEY)
      status = savedhrfragment(outfile,x1,y1,dhrscale);
    else
      status = ESCKEY;

    SetCrtMode(TEXT);

  }

  if (status != ESCKEY) {
    if (status == SUCCESS)printf("%s.AUX & .BIN and %s.2FC Saved!\n",outfile,outfile);
    else printf("Error saving %s.AUX & .BIN and %s.2FC!\n",outfile,outfile);
  }
  else printf("Exiting without saving...\n");

  free(rawbuffer);
  puts("Have a Nice Dos!");
  exit(0);

}
